package org.yu.team2023.flashdays.registries;


import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import lombok.Getter;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.DataGenerator;
import net.minecraft.tags.TagKey;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraftforge.common.util.NonNullFunction;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;


public class SimpleReg {
    @Getter
    protected final String id;
    protected final Multimap<Supplier<CreativeModeTab>, Supplier<? extends ItemLike>> creativeTabMap = HashMultimap.create();
    protected final DeferredRegister<Item> itemDeferredRegister;
    protected final DeferredRegister<Block> blockDeferredRegister;
    protected final DeferredRegister<MobEffect> mobEffectDeferredRegister;
    protected final DeferredRegister<CreativeModeTab> creativeModeTabDeferredRegister;
    protected final DeferredRegister<Attribute> attributeDeferredRegister;
    protected final Multimap<Supplier<? extends Item>, TagKey<Item>> itemTags = HashMultimap.create();
    protected final List<TagKey<Block>> blockTagsCopyList = new ArrayList<>();
    protected final Multimap<Supplier<? extends Block>, TagKey<Block>> blockTags = HashMultimap.create();
    protected final Map<Supplier<String>,String> langCH=new HashMap<>();
    protected final Map<Supplier<? extends ItemLike>, BiConsumer<Supplier<? extends ItemLike>,SimpleItemModelProvider>> itemModels=new HashMap<>();
    protected final Map<Supplier<? extends Block>, BiConsumer<Supplier<? extends Block>,SimpleBlockStateProvider>> blockModelMap=new HashMap<>();
    @Getter
    @Nullable
    protected RegistryObject<CreativeModeTab> modTab;

    public SimpleReg(String id) {
        this.id = id;
        itemDeferredRegister = DeferredRegister.create(ForgeRegistries.ITEMS, id);
        blockDeferredRegister = DeferredRegister.create(ForgeRegistries.BLOCKS, id);
        this.mobEffectDeferredRegister=DeferredRegister.create(ForgeRegistries.MOB_EFFECTS,id);
        creativeModeTabDeferredRegister = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, id);
        this.attributeDeferredRegister=DeferredRegister.create(ForgeRegistries.ATTRIBUTES,id);
        var bus = FMLJavaModLoadingContext.get().getModEventBus();
        itemDeferredRegister.register(bus);
        blockDeferredRegister.register(bus);
        mobEffectDeferredRegister.register(bus);
        attributeDeferredRegister.register(bus);
        creativeModeTabDeferredRegister.register(bus);
        bus.addListener(this::onTabEvent);
        bus.addListener(this::onDataEvent);
    }

    private void onTabEvent(BuildCreativeModeTabContentsEvent event) {
        creativeTabMap.keySet().forEach(tabSupplier -> {
            if (event.getTab().equals(tabSupplier.get())){
                creativeTabMap.get(tabSupplier)
                        .stream()
                        .sorted((o1, o2) -> {
                            String key1 = ForgeRegistries.ITEMS.getKey(o1.get().asItem()).toString();
                            String key2 = ForgeRegistries.ITEMS.getKey(o2.get().asItem()).toString();
                            return key1.compareTo(key2);
                        }).forEach(event::accept);
            }
        });
    }

    private void onDataEvent(GatherDataEvent event) {
        DataGenerator generator = event.getGenerator();
        if (event.includeServer()) {
            SimpleBlockTagProvider blockTagProvider = new SimpleBlockTagProvider(generator.getPackOutput(), event.getLookupProvider(), id, event.getExistingFileHelper(), blockTags);
            SimpleItemTagProvider itemTagProvider = new SimpleItemTagProvider(generator.getPackOutput(), event.getLookupProvider(), blockTagProvider.contentsGetter(), id, event.getExistingFileHelper(), itemTags, blockTagsCopyList);
            generator.addProvider(true, blockTagProvider);
            generator.addProvider(true, itemTagProvider);
        }
        if(event.includeClient()){
            generator.addProvider(true,new SimpleEnLangProvider(generator.getPackOutput(),id));
            generator.addProvider(true,new SimpleCNLangProvider(generator.getPackOutput(),id,langCH));
            generator.addProvider(true,new SimpleItemModelProvider(generator.getPackOutput(),id,event.getExistingFileHelper(),itemModels));
            generator.addProvider(true,new SimpleBlockStateProvider(generator.getPackOutput(),id,event.getExistingFileHelper(),blockModelMap));
        }
    }

    public <T extends Item> ItemBuilder<T, SimpleReg> item(String id, NonNullFunction<Item.Properties, T> factory) {
        return new ItemBuilder<>(this, id, factory, this);
    }

    public <T extends Block> BlockBuilder<T> block(String id, NonNullFunction<BlockBehaviour.Properties, T> factory) {
        return new BlockBuilder<>(this, id, factory);
    }

    public RegistryObject<Item> simpleItem(String id, Supplier<CreativeModeTab> tabSupplier) {
        return item(id, Item::new).tab(tabSupplier).build();
    }

    public RegistryObject<CreativeModeTab> tab(String name, Consumer<CreativeModeTab.Builder> consumer) {
        return creativeModeTabDeferredRegister.register(name, () -> {
            var builder = CreativeModeTab.builder();
            consumer.accept(builder);
            return builder.build();
        });
    }

    public RegistryObject<CreativeModeTab> modTab(Consumer<CreativeModeTab.Builder> consumer) {
        modTab = tab(id, consumer);
        return modTab;
    }

    @SafeVarargs
    public final SimpleReg addItemTag(Supplier<? extends Item> item, TagKey<Item>... itemKey) {
        itemTags.putAll(item, Arrays.stream(itemKey).toList());
        return this;
    }

    @SafeVarargs
    public final SimpleReg addBlockTag(Supplier<? extends Block> item, TagKey<Block>... itemKey) {
        blockTags.putAll(item, Arrays.stream(itemKey).toList());
        return this;
    }
    public SimpleReg lang(String key,String value){
        return this.lang(()->key,value);
    }
    public SimpleReg lang(Supplier<String> key,String value){
        langCH.put(key,value);
        return this;
    }
    public <T extends MobEffect> RegistryObject<T> mobEffect(String name,String cn,Supplier<T> creator){
        RegistryObject<T> result=mobEffectDeferredRegister.register(name,creator);
        langCH.put(()->result.get().getDescriptionId(),cn);
        return result;
    }
    public  RegistryObject<Attribute> attribute(String name,String cn,double defaultValue,double from,double to,boolean sync){
        RegistryObject<Attribute> result=attributeDeferredRegister.register(name,()->new RangedAttribute("entity."+name,defaultValue,from,to).setSyncable(sync));
        langCH.put(()->result.get().getDescriptionId(),cn);
        return result;
    }

    @SafeVarargs
    public final SimpleReg copyBlockTag(TagKey<Block>... itemKey) {
        blockTagsCopyList.addAll(Arrays.stream(itemKey).toList());
        return this;
    }
}
